--!native
MU = require(script.Parent.MathUtil);
Mat2 = require(script.Parent.Mat2);
Quaternion = require(script.Parent.Quaternion);

--[[
    在MathUtil的基础上, 进一步封装了一些和几何体操作相关的函数.
]]
local m = {}

-- 旋转一个cFrame. 
-- axis 是一个单位向量代表旋转轴
-- angle 是一个代表要旋转角度的数字，以弧度为单位
function RotateCFrame(cFrame, axis, angle)
	local rotation = CFrame.fromAxisAngle(axis, angle)
	return cFrame * rotation;
end

--- angle is in radian
function m.RotateVector(v, axis, angle)
	return CFrame.fromAxisAngle(axis, angle):VectorToWorldSpace(v);
end

--- 让向量v以a角度绕着原点随机展开(注意a是弧度!)
--- 可以用它来实现散弹枪子弹的随机飞行角度
function m.RandomVector(v, a)
	--local axis = m.RndOrthogonal(v);
    local axis = MU.RndOrthogonal(v);
    --return m.RotateVector(v, axis, MU.RandF(-a, a)); 
    return Quaternion.FromAngleAxis(MU.RandF(-a, a), axis):RotateVector(v);
end

--- 旋转一个2D向量(Vec2)
function m.RotateVec2(v2, angle)
    return Mat2.ByAngle(angle):MulVec2(v2);
end

--- 返回随机旋转一个2D向量(Vec2)后的结果. 随机范围是(-angle, angle)
--- angle是弧度
function m.RndRotateVec2(v2, angle)
    return Mat2.ByAngle(MU.RandF(-angle, angle)):MulVec2(v2);
end

--- 返回XZ平面上的圆形上的一个随机点
--- 的中心在Center, 半径为radius
function m.RndPtOnCircleXZ(center, radius)
	local x, z = MU.OnUnitCircle2();
	return center + Vector3.new(x * radius, 0, z * radius);
end

local function _Sign(n) if n > 0 then return 1; elseif n < 0 then return -1; else return 0; end end
--- 将向量投影到XZ坐标轴上(Y始终为0)
function m.ProjectV3ToAxisXZ(v)
	local castDir = nil;
	if v.X == 0 and v.Z == 0 then
		castDir = Vector3.new(0, 0, -1);
	else
		if math.abs(v.X) >= math.abs(v.Z) then
			castDir = Vector3.new(_Sign(v.X), 0, 0);
		else
			castDir = Vector3.new(0, 0, _Sign(v.Z));
		end	
	end
    return castDir;
end

--- Swing-Twist分解
--- 参考: https://github.com/EgoMoose/Articles/blob/master/CFrames/Advanced%20CFrame%20tricks.md
--- https://allenchou.net/2018/05/game-math-swing-twist-interpolation-sterp/
function m.SwingTwist(cf, direction)
	local axis, theta = cf:ToAxisAngle()
	-- convert to quaternion
	local w, v = math.cos(theta/2),  math.sin(theta/2)*axis

	-- plug qp into the constructor and it will be normalized automatically
	local proj = v:Dot(direction)*direction
	local twist = CFrame.new(0, 0, 0, proj.x, proj.y, proj.z, w)

	-- cf = swing * twist, thus...
	local swing = cf * twist:Inverse()

	return swing, twist
end

return m;